﻿using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using FlowgraphViewer.Model;
using System.Collections.Generic;
using FlowgraphViewer.Services;
using System;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using System.Windows.Media.Animation;
using SamNeirinck.ExtensionMethods;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;

namespace FlowgraphViewer.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private ObservableCollection<FlowNodeInstance> _nodes;

        public ObservableCollection<FlowNodeInstance> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }

        public ObservableCollection<Edge> Edges { get; set; }
        

        private IEnumerable<FlowNode> _availableNodes;

        // Commands
        public ICommand ShowContextMenu { get; set; }
        public ICommand HandledCommand { get; set; }
        public ICommand ZoomCommand { get; set; }
        public ICommand StartPanning { get; set; }
        public ICommand StopPanning { get; set; }
        public ICommand UpdatePanning { get; set; }


        // Panning vars
        private bool _moving = false;
        private Point _clickPoint;
        private double _x;
        private double _y;


        private double _nodesX;

        public double NodesX
        {
            get { return _nodesX; }
            set { _nodesX = value; RaisePropertyChanged("NodesX"); }
        }

        private double _nodexY;

        public double NodesY
        {
            get { return _nodexY; }
            set { _nodexY = value; RaisePropertyChanged("NodesY"); }
        }


        private double _browserWidth;

        public double BrowserWidth
        {
            get { return _browserWidth; }
            set { _browserWidth = value; RaisePropertyChanged("BrowserWidth"); }
        }


        private double _browserHeight;

        public double BrowserHeight
        {
            get { return _browserHeight; }
            set { _browserHeight = value; RaisePropertyChanged("BrowserHeight"); }
        }

        private string _resolutionText;

        public string ResolutionText
        {
            get { return _resolutionText; }
            set { _resolutionText = value; RaisePropertyChanged("ResolutionText"); }
        }

        private string _fpsText;

        public string FPSText
        {
            get { return _fpsText; }
            set { _fpsText = value; RaisePropertyChanged("FPSText"); }
        }

        private double _zoomLevel;
        public double ZoomLevel
        {
            get { return _zoomLevel; }
            set
            {
                _zoomLevel = value;
                _zoomLevel = _zoomLevel.Clamp(MinZoomLevel, MaxZoomLevel);
                RaisePropertyChanged("ZoomLevel");
            }
        }

        private double _minZoomLevel;

        public double MinZoomLevel
        {
            get { return _minZoomLevel; }
            set
            {
                _minZoomLevel = value;
            }
        }

        private double _maxZoomLevel;

        public double MaxZoomLevel
        {
            get { return _maxZoomLevel; }
            set { _maxZoomLevel = value; }
        }




        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            InitializeCommands();

            _nodes = new ObservableCollection<FlowNodeInstance>();
            Edges = new ObservableCollection<Edge>();
            _availableNodes = CrysisNodeService.Instance.RegisterNodes();

            MinZoomLevel = 0.1;
            MaxZoomLevel = 2;
            ZoomLevel = 1;

            App.Current.Host.Content.Resized += (sender, args) =>
                {
                    BrowserWidth = App.Current.Host.Content.ActualWidth;
                    BrowserHeight = App.Current.Host.Content.ActualHeight;
                    ResolutionText = String.Format("Available space: {0}x{1}", BrowserWidth, BrowserHeight);
                    FPSText = String.Format("FPS: {0}", 0);

                };

            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
            }
            else
            {
                // Code runs "for real"
                LoadSampleFlowgraph();

            }
        }

        private void InitializeCommands()
        {

            ShowContextMenu = new RelayCommand(() =>
                {
                    
                    
                });

            HandledCommand = new RelayCommand<MouseButtonEventArgs>((e) => e.Handled = true);


            ZoomCommand = new RelayCommand<MouseWheelEventArgs>(
                (e) =>
                {
                    if (e.Delta > 0)
                    {

                        ZoomLevel += 0.1;
                    }
                    else
                    {
                        ZoomLevel -= 0.1;
                    }
                });

            StartPanning = new RelayCommand<MouseButtonEventArgs>(e =>
                {
                    _clickPoint = e.GetPosition((UIElement)e.OriginalSource);

                    _x = NodesX;
                    _y = NodesY;
                    _moving = true;
                });

            StopPanning = new RelayCommand<MouseButtonEventArgs>(e =>
                {
                    _moving = false;
                });

            UpdatePanning = new RelayCommand<MouseEventArgs>(e =>
                {
                    if (_moving)
                    {
                        Point click = e.GetPosition((UIElement)e.OriginalSource);

                        NodesX = _x - (_clickPoint.X - click.X) * 1 / ZoomLevel;
                        NodesY = _y - (_clickPoint.Y - click.Y) * 1 / ZoomLevel;
                    }
                });

        }

        private void LoadSampleFlowgraph()
        {

            string xml = @"<Graph Description="""" Group="""" enabled=""1"" MultiPlayer=""ClientServer"">
 <Nodes>
  <Node Id=""1"" Class=""Start"" pos=""-310,40,0"" flags=""0"">
   <Inputs InGame=""1"" InEditor=""1""/>
  </Node>
  <Node Id=""2"" Class=""HUD:ShowHUDMessage"" pos=""250,-130,0"" flags=""0"">
   <Inputs text_Message=""This is a very long message"" Pos=""1"" clr_Color=""1,1,1"" Time=""3""/>
  </Node>
  <Node Id=""3"" Class=""HUD:Binoculars"" pos=""-20,50,0"" flags=""1"">
   <Inputs TaggedEntity=""0""/>
  </Node>
 </Nodes>
 <Edges>
  <Edge nodeIn=""2"" nodeOut=""1"" portIn=""Trigger"" portOut=""output"" enabled=""1""/>
  <Edge nodeIn=""3"" nodeOut=""1"" portIn=""Show"" portOut=""output"" enabled=""1""/>
  <Edge nodeIn=""2"" nodeOut=""3"" portIn=""text_Message"" portOut=""Activated"" enabled=""1""/>
 </Edges>
</Graph>
";

            Flowgraph fg = FlowgraphXmlParser.GetFlowgraph(xml);

            if (fg != null)
            {
                foreach (var item in fg.Nodes)
                {
                    Nodes.Add(item);
                }

                foreach (var item in fg.Edges)
                {
                    Edges.Add(item);
                }
            }



            /*int i = 0;
            int limit = 10;

            foreach (var node in _availableNodes)
            {
                if (i > limit)
                    break;

                limit++;
                _nodes.Add(node);
            }*/



        }

        ////public override void Cleanup()
        ////{
        ////    // Clean up if needed

        ////    base.Cleanup();
        ////}
    }
}